/**
 * VSCode Rsync部署插件 - Rsync管理器
 * 负责执行rsync命令和文件同步操作
 * 
 * @author lihengtt <lihengsir@gmail.com>
 * @github https://github.com/lihengtt
 */

import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import { spawn, ChildProcess } from 'child_process';
import { ConfigManager } from './configManager';

export interface SyncResult {
    success: boolean;
    output: string;
    error?: string;
}

export class RsyncManager {
    private configManager: ConfigManager;
    private outputChannel: vscode.OutputChannel;
    private runningProcesses: Set<ChildProcess> = new Set();

    /**
     * 构造函数
     * @param configManager - 配置管理器
     * @author lihengtt <lihengsir@gmail.com>
     */
    constructor(configManager: ConfigManager) {
        this.configManager = configManager;
        this.outputChannel = vscode.window.createOutputChannel('Rsync Deployment');
    }

    /**
     * 同步单个文件
     * @param localFilePath - 本地文件路径
     * @returns Promise<SyncResult>
     * @author lihengtt <lihengsir@gmail.com>
     */
    async syncFile(localFilePath: string): Promise<SyncResult> {
        const config = this.configManager.getConfig();
        if (!config) {
            return { success: false, output: '', error: '配置未找到，请先配置服务器信息' };
        }

        if (!fs.existsSync(localFilePath)) {
            return { success: false, output: '', error: `本地文件不存在: ${localFilePath}` };
        }

        this.outputChannel.appendLine(`[开始] 同步文件: ${path.basename(localFilePath)}`);
        this.outputChannel.appendLine(`[源路径] ${localFilePath}`);
        
        try {
            // 计算相对路径
            const relativePath = path.relative(config.localPath, localFilePath);
            const remotePath = path.posix.join(config.remotePath, relativePath.replace(/\\/g, '/'));
            
            this.outputChannel.appendLine(`[目标路径] ${config.serverInfo.username}@${config.serverInfo.host}:${remotePath}`);
            this.outputChannel.appendLine(`[时间] ${new Date().toLocaleString()}`);
            
            const rsyncArgs = this.buildRsyncArgs(localFilePath, remotePath, false);
            const result = await this.executeRsync(rsyncArgs);
            
            if (result.success) {
                this.outputChannel.appendLine(`[成功] 文件同步完成: ${path.basename(localFilePath)}`);
            } else {
                this.outputChannel.appendLine(`[失败] 文件同步失败: ${path.basename(localFilePath)}`);
                this.outputChannel.appendLine(`[错误] ${result.error}`);
            }
            
            this.outputChannel.appendLine(`[结束] ${new Date().toLocaleString()}\n`);
            return result;
        } catch (error) {
            const errorMsg = `同步文件时发生错误: ${error}`;
            this.outputChannel.appendLine(`[异常] ${errorMsg}`);
            this.outputChannel.appendLine(`[结束] ${new Date().toLocaleString()}\n`);
            return { success: false, output: '', error: errorMsg };
        }
    }

    /**
     * 同步所有文件
     * @returns Promise<SyncResult>
     * @author lihengtt <lihengsir@gmail.com>
     */
    async syncAll(): Promise<SyncResult> {
        const config = this.configManager.getConfig();
        if (!config) {
            return { success: false, output: '', error: '配置未找到，请先配置服务器信息' };
        }

        if (!fs.existsSync(config.localPath)) {
            return { success: false, output: '', error: `本地路径不存在: ${config.localPath}` };
        }

        this.outputChannel.appendLine(`[开始] 同步所有文件`);
        this.outputChannel.appendLine(`[源路径] ${config.localPath}`);
        this.outputChannel.appendLine(`[目标路径] ${config.serverInfo.username}@${config.serverInfo.host}:${config.remotePath}`);
        this.outputChannel.appendLine(`[时间] ${new Date().toLocaleString()}`);
        
        try {
            const rsyncArgs = this.buildRsyncArgs(config.localPath + '/', config.remotePath, false);
            const result = await this.executeRsync(rsyncArgs);
            
            if (result.success) {
                this.outputChannel.appendLine(`[成功] 所有文件同步完成`);
            } else {
                this.outputChannel.appendLine(`[失败] 文件同步失败`);
                this.outputChannel.appendLine(`[错误] ${result.error}`);
            }
            
            this.outputChannel.appendLine(`[结束] ${new Date().toLocaleString()}\n`);
            return result;
        } catch (error) {
            const errorMsg = `同步所有文件时发生错误: ${error}`;
            this.outputChannel.appendLine(`[异常] ${errorMsg}`);
            this.outputChannel.appendLine(`[结束] ${new Date().toLocaleString()}\n`);
            return { success: false, output: '', error: errorMsg };
        }
    }

    /**
     * 下载文件
     * @param remotePath - 远程文件路径
     * @param localPath - 本地保存路径
     * @returns Promise<SyncResult>
     * @author lihengtt <lihengsir@gmail.com>
     */
    async downloadFile(remotePath: string, localPath: string): Promise<SyncResult> {
        const config = this.configManager.getConfig();
        if (!config) {
            return { success: false, output: '', error: '配置未找到，请先配置服务器信息' };
        }

        this.outputChannel.appendLine(`[开始] 下载文件: ${path.basename(remotePath)}`);
        this.outputChannel.appendLine(`[源路径] ${config.serverInfo.username}@${config.serverInfo.host}:${remotePath}`);
        this.outputChannel.appendLine(`[目标路径] ${localPath}`);
        this.outputChannel.appendLine(`[时间] ${new Date().toLocaleString()}`);
        
        try {
            const rsyncArgs = this.buildRsyncArgs(localPath, remotePath, true);
            const result = await this.executeRsync(rsyncArgs);
            
            if (result.success) {
                this.outputChannel.appendLine(`[成功] 文件下载完成: ${path.basename(remotePath)}`);
                this.outputChannel.appendLine(`[详情] 文件已保存到本地: ${localPath}`);
            } else {
                this.outputChannel.appendLine(`[失败] 文件下载失败: ${path.basename(remotePath)}`);
                this.outputChannel.appendLine(`[错误] ${result.error}`);
            }
            
            this.outputChannel.appendLine(`[结束] ${new Date().toLocaleString()}\n`);
            return result;
        } catch (error) {
            const errorMsg = `下载文件时发生错误: ${error}`;
            this.outputChannel.appendLine(`[异常] ${errorMsg}`);
            this.outputChannel.appendLine(`[结束] ${new Date().toLocaleString()}\n`);
            return { success: false, output: '', error: errorMsg };
        }
    }

    /**
     * 构建rsync命令参数
     * @param localPath - 本地路径
     * @param remotePath - 远程路径
     * @param isDownload - 是否为下载操作
     * @returns string[]
     * @author lihengtt <lihengsir@gmail.com>
     */
    private buildRsyncArgs(localPath: string, remotePath: string, isDownload: boolean = false): string[] {
        const config = this.configManager.getConfig();
        if (!config) {
            throw new Error('配置未找到');
        }

        const args: string[] = ['-avz', '--progress'];
        
        // 添加排除规则
        if (config.excludeRules && config.excludeRules.length > 0) {
            config.excludeRules.forEach(rule => {
                args.push('--exclude', rule);
            });
        }

        // SSH连接参数
        const sshArgs: string[] = [];
        
        // 端口设置
        if (config.serverInfo.port && config.serverInfo.port !== 22) {
            sshArgs.push(`-p ${config.serverInfo.port}`);
        }
        
        // 认证方式：优先使用私钥，否则使用密码
        if (config.serverInfo.privateKeyPath && config.serverInfo.privateKeyPath.trim() !== '') {
            sshArgs.push(`-i "${config.serverInfo.privateKeyPath}"`);
            args.push('-e', `ssh ${sshArgs.join(' ')}`);
        } else if (config.serverInfo.password) {
            // 对于Windows系统，提示用户安装sshpass或使用私钥
            if (process.platform === 'win32') {
                throw new Error('Windows系统不支持密码认证，请使用SSH私钥认证或安装WSL');
            }
            // 使用sshpass进行密码认证（仅限Linux/macOS）
            args.unshift('sshpass', '-p', config.serverInfo.password);
            if (sshArgs.length > 0) {
                args.push('-e', `ssh ${sshArgs.join(' ')}`);
            }
        } else {
            throw new Error('请提供私钥路径或密码进行SSH认证');
        }

        // 源和目标路径
        const remoteTarget = `${config.serverInfo.username}@${config.serverInfo.host}:${remotePath}`;
        
        if (isDownload) {
            args.push(remoteTarget, localPath);
        } else {
            args.push(localPath, remoteTarget);
        }

        return args;
    }

    /**
     * 执行rsync命令
     * @param args - rsync命令参数
     * @returns Promise<SyncResult>
     * @author lihengtt <lihengsir@gmail.com>
     */
    private executeRsync(args: string[]): Promise<SyncResult> {
        return new Promise((resolve) => {
            this.outputChannel.appendLine(`执行命令: rsync ${args.join(' ')}`);
            
            // 添加stdio配置避免EBADF错误
            const rsync: ChildProcess = spawn('rsync', args, {
                stdio: ['ignore', 'pipe', 'pipe']
            });
            
            // 跟踪正在运行的进程
            this.runningProcesses.add(rsync);
            const output = '';
            let error = '';

            rsync.stdout?.on('data', (data) => {
                const output = data.toString();
                this.outputChannel.appendLine(`[rsync输出] ${output}`);
                // 可以在这里解析rsync的输出来显示进度
            });

            rsync.stderr?.on('data', (data) => {
                const text = data.toString();
                error += text;
                this.outputChannel.append(text);
            });

            rsync.on('close', (code) => {
                // 从跟踪列表中移除已完成的进程
                this.runningProcesses.delete(rsync);
                
                const success = code === 0;
                this.outputChannel.appendLine(`命令执行完成，退出代码: ${code}`);
                
                resolve({
                    success,
                    output,
                    error: success ? undefined : error
                });
            });

            rsync.on('error', (err) => {
                // 从跟踪列表中移除出错的进程
                this.runningProcesses.delete(rsync);
                
                const errorMsg = `执行rsync命令时发生错误: ${err.message}`;
                this.outputChannel.appendLine(errorMsg);
                resolve({
                    success: false,
                    output: '',
                    error: errorMsg
                });
            });
        });
    }

    /**
     * 显示输出通道
     * @author lihengtt <lihengsir@gmail.com>
     */
    showOutput(): void {
        this.outputChannel.show();
    }

    /**
     * 测试SSH连接
     * @returns Promise<SyncResult>
     * @author lihengtt <lihengsir@gmail.com>
     */
    async testConnection(): Promise<SyncResult> {
        const config = this.configManager.getConfig();
        if (!config) {
            return { success: false, output: '', error: '配置未找到，请先配置服务器信息' };
        }

        this.outputChannel.appendLine(`[开始] 测试SSH连接`);
        this.outputChannel.appendLine(`[服务器] ${config.serverInfo.username}@${config.serverInfo.host}:${config.serverInfo.port}`);
        this.outputChannel.appendLine(`[时间] ${new Date().toLocaleString()}`);
        
        try {
            // 构建SSH测试命令参数
            const sshArgs = this.buildSSHTestArgs();
            const result = await this.executeSSHTest(sshArgs);
            
            if (result.success) {
                this.outputChannel.appendLine(`[成功] SSH连接测试成功`);
                this.outputChannel.appendLine(`[详情] 服务器响应正常`);
            } else {
                this.outputChannel.appendLine(`[失败] SSH连接测试失败`);
                this.outputChannel.appendLine(`[错误] ${result.error}`);
            }
            
            this.outputChannel.appendLine(`[结束] ${new Date().toLocaleString()}\n`);
            return result;
        } catch (error) {
            const errorMsg = `测试SSH连接时发生错误: ${error}`;
            this.outputChannel.appendLine(`[异常] ${errorMsg}`);
            this.outputChannel.appendLine(`[结束] ${new Date().toLocaleString()}\n`);
            return { success: false, output: '', error: errorMsg };
        }
    }

    /**
     * 构建SSH测试命令参数
     * @returns string[]
     * @author lihengtt <lihengsir@gmail.com>
     */
    private buildSSHTestArgs(): string[] {
        const config = this.configManager.getConfig();
        if (!config) {
            throw new Error('配置未找到');
        }

        const args: string[] = [];
        
        // SSH连接参数
        const sshArgs: string[] = [];
        
        // 端口设置
        if (config.serverInfo.port && config.serverInfo.port !== 22) {
            sshArgs.push('-p', config.serverInfo.port.toString());
        }
        
        // 连接超时设置
        sshArgs.push('-o', 'ConnectTimeout=10');
        sshArgs.push('-o', 'StrictHostKeyChecking=no');
        sshArgs.push('-o', 'BatchMode=yes');
        
        // 认证方式：优先使用私钥，否则使用密码
        if (config.serverInfo.privateKeyPath && config.serverInfo.privateKeyPath.trim() !== '') {
            sshArgs.push('-i', config.serverInfo.privateKeyPath);
        } else if (config.serverInfo.password) {
            // 对于Windows系统，提示用户安装sshpass或使用私钥
            if (process.platform === 'win32') {
                throw new Error('Windows系统不支持密码认证，请使用SSH私钥认证或安装WSL');
            }
            // 使用sshpass进行密码认证（仅限Linux/macOS）
            args.push('sshpass', '-p', config.serverInfo.password);
        } else {
            throw new Error('请提供私钥路径或密码进行SSH认证');
        }

        // 添加SSH参数
        args.push('ssh');
        args.push(...sshArgs);
        
        // 目标服务器和测试命令
        args.push(`${config.serverInfo.username}@${config.serverInfo.host}`);
        args.push('echo "SSH连接测试成功"');

        return args;
    }

    /**
     * 执行SSH测试命令
     * @param args - SSH命令参数
     * @returns Promise<SyncResult>
     * @author lihengtt <lihengsir@gmail.com>
     */
    private executeSSHTest(args: string[]): Promise<SyncResult> {
        return new Promise((resolve) => {
            this.outputChannel.appendLine(`执行命令: ${args.join(' ')}`);
            
            const command = args[0];
            const commandArgs = args.slice(1);
            // 添加stdio配置避免EBADF错误
            const ssh: ChildProcess = spawn(command, commandArgs, {
                stdio: ['ignore', 'pipe', 'pipe']
            });
            
            // 跟踪正在运行的进程
            this.runningProcesses.add(ssh);
            const result = { stdout: '', stderr: '' };
            let output = '';
            let error = '';

            ssh.stdout?.on('data', (data) => {
                const output = data.toString();
                this.outputChannel.appendLine(`[SSH输出] ${output}`);
                result.stdout += output;
            });

            ssh.stderr?.on('data', (data) => {
                const text = data.toString();
                error += text;
                this.outputChannel.append(text);
            });

            ssh.on('close', (code) => {
                // 从跟踪列表中移除已完成的进程
                this.runningProcesses.delete(ssh);
                
                const success = code === 0;
                this.outputChannel.appendLine(`命令执行完成，退出代码: ${code}`);
                
                resolve({
                    success,
                    output,
                    error: success ? undefined : error || '连接失败'
                });
            });

            ssh.on('error', (err) => {
                // 从跟踪列表中移除出错的进程
                this.runningProcesses.delete(ssh);
                
                const errorMsg = `执行SSH测试命令时发生错误: ${err.message}`;
                this.outputChannel.appendLine(errorMsg);
                resolve({
                    success: false,
                    output: '',
                    error: errorMsg
                });
            });
        });
    }

    /**
     * 终止所有正在运行的进程
     * 在插件停用时调用，确保不会有孤儿进程
     * @author lihengtt <lihengsir@gmail.com>
     */
    killAllProcesses(): void {
        if (this.runningProcesses.size > 0) {
            this.outputChannel.appendLine(`正在终止 ${this.runningProcesses.size} 个运行中的进程...`);
            
            for (const process of this.runningProcesses) {
                try {
                    if (!process.killed) {
                        process.kill('SIGTERM');
                        // 如果SIGTERM无效，使用SIGKILL强制终止
                        setTimeout(() => {
                            if (!process.killed) {
                                process.kill('SIGKILL');
                            }
                        }, 5000);
                    }
                } catch (error) {
                    this.outputChannel.appendLine(`终止进程时发生错误: ${error}`);
                }
            }
            
            this.runningProcesses.clear();
            this.outputChannel.appendLine('所有rsync进程已终止');
        }
    }

    /**
     * 清空输出通道
     * @author lihengtt <lihengsir@gmail.com>
     */
    clearOutput(): void {
        this.outputChannel.clear();
    }
}